原始题目:剑指 Offer 30. 包含min函数的栈 (opens new window)
解题思路:
可以借助辅助栈,用来保存当前主栈中的最小值,具体如下:
首先有两个栈 和 ,负责存储压入和弹出所有元素, 负责保存当前元素中的最小值。下面通过一个例子来说明,也可以直接看结论。
压入阶段
- 压入的时候, 正常压入,因为当前 是空的,所以 压入 中;
- 压入的时候, 正常压入,因为当前 的栈顶元素 大于 ,所以 压入 ;
- 压入的时候, 正常压入,因为当前 的栈顶元素 小于 ,所以 不压入 ;
- 压入的时候, 正常压入,因为当前 的栈顶元素 大于 ,所以 压入 ;
- 压入的时候, 正常压入,因为当前 的栈顶元素 小于 ,所以 不压入 ;
弹出阶段
- 当 弹出时, 正常弹出,因为当前 的栈顶元素 不等于 ,所以 不弹出;
- 当 弹出时, 正常弹出,因为当前 的栈顶元素 等于 ,所以 弹出;
- 当 弹出时, 正常弹出,因为当前 的栈顶元素 不等于 ,所以 不弹出;
- 当 弹出时, 正常弹出,因为当前 的栈顶元素 等于 ,所以 弹出;
- 当 弹出时, 正常弹出,因为当前 的栈顶元素 等于 ,所以 弹出;
总结
压入元素 时, 都是正常压入,而 则需要判断当前的栈顶元素 是否大于等于 ,如果大于 ,说明此时 $a $已经不是最小的了,把 压入 ;否则不压入 。
为什么是大于等于?
如果连续压入两个最小元素,比如压入两个 ,那么 也要压入两次 ,这是因为弹出时,是通过判断 和 $S2 $ 栈顶元素是否相等来决定是否要弹出 的。因此在 的时候,也要将 压入 。
弹出元素 时, 都是正常弹出,而 则需要判断当前的栈顶元素 是否等于 ,如果等于,说明 是在 压入时,一起压入 的,需要把 弹出。否则不弹出 栈顶元素
代码:
class MinStack {
/**
* mainStack 存放着所有的元素
*/
private final Deque<Integer> mainStack;
/**
* minStack 的栈顶存放着 mainStack 中的最小值
*/
private final Deque<Integer> minStack;
/**
* initialize your data structure here.
*/
public MinStack() {
mainStack = new LinkedList<>();
minStack = new LinkedList<>();
}
public void push(int x) {
mainStack.push(x);
if (minStack.isEmpty() || minStack.peek() >= x) {
minStack.push(x);
}
}
public void pop() {
// 如果弹出的不是最小值,那么 minStack 就不用动
// 如果弹出的是最小值,那么 minStack 只需要把栈顶弹出即可
if (mainStack.pop().equals(minStack.peek())) {
minStack.pop();
}
}
public int top() {
return mainStack.isEmpty() ? -1 : mainStack.peek();
}
public int min() {
return minStack.isEmpty() ? -1 : minStack.peek();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
复杂度分析
- 时间复杂度:
push()
,pop()
,top()
,min()
四个函数的时间复杂度均为常数级别。 - 空间复杂度:当共有 个待入栈元素时,辅助栈 最差情况下存储 个元素,使用 额外空间。